home *** CD-ROM | disk | FTP | other *** search
-
- /* Copyright (C) 2009 Norman Solomon
- * e-mail: historytree.addon@yahoo.com
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the License.
- */
-
- // **************************************************************************
- // ***** *****
- // ***** TREE-VIEW DRAWING FUNCTIONS - DRAWING ON THE gCanvas *****
- // ***** -------------------------------------------------------- *****
- // ***** 1) Calls "treeArranger.js" methods which layout the tree *****
- // ***** 2) Draws TreeNode's on gCtx and expands ".jpeg" images *****
- // ***** *****
- // **************************************************************************
-
- // =============================================================
- // Lays out all TreeNode.(x,y) then "plants" and draws the tree
- // -------------------------------------------------------------
- // *** HOWEVER - Scrolling that depends on TreeNode.(x,y)
- // values CANNOT be done until after the tree has been laid out
- // =============================================================
- function drawTree(scrollToCorner, scrollTabID, scrollNode)
- {
- // Init vars used in this function
- var treeRoot = gTreeNodes[0]; // Assumed
- var leftBorder; // Integer
- var dimen; // Point()
- var tNode; // TreeNode
- var canvasHgt; // Integer
-
- // ------------------------------------------------
- // Stop GV display setInterval() if its running
- if (gGVdispInterval !== null)
- {
- clearInterval(gGVdispInterval);
- gGVdispInterval = null;
- }
-
- // ---------------------------------------------------------
- // Layout tree out horizontally then changes it to vertical
- // (Sven Moen's algorithm ALWAYS lays out tree horizontally)
- tArrLayout(treeRoot);
- changeTreeToVerticalLayout(treeRoot);
-
- // Get the tree's dimensions as a Point(width, height)
- dimen = treeDimensions();
-
- // Set <canvas...> width to enable full tree viewing
- // Also making sure that gCanvas fills the screen width
- if (dimen.x + 40 < CANVAS_MIN_WID)
- gCanvas.width = CANVAS_MIN_WID;
- else
- gCanvas.width = dimen.x + 40;
-
- // Set <canvas...> height, where the Y add on allows the
- // user to view the lowest expanded hNode ".jpeg" image
- canvasHgt = Math.round(dimen.y + (MAX_IMG_WID * 0.65) + 70);
- if (canvasHgt < CANVAS_MIN_HGT)
- gCanvas.height = CANVAS_MIN_HGT;
- else
- gCanvas.height = canvasHgt;
-
- // Centralize the tree horizontally on the <canvas...>
- leftBorder = Math.round((gCanvas.width - dimen.x) / 2);
- setLeftAndTopBorderForWholeTree(leftBorder, 28);
-
- // ----------------------------------------------------
- // Clear the gCanvas - i.e. the only window <canvas...>
- gCtx.fillStyle = CANVAS_BAK_COL; // Dark grey
- gCtx.fillRect(0, 0, gCanvas.width, gCanvas.height);
-
- // Scroll using passed params and/or TreeNode.(x,y) values
- if (scrollTabID !== null)
- {
- // Scroll passed scrollTabID into view
- scrollTabIntoView(scrollTabID);
- }
- else if (scrollNode !== null)
- {
- // Scroll passed scrollNode into view
- scrollNodeIntoView(scrollNode);
- }
- else if (scrollToCorner)
- {
- // Scroll to top-left corner
- gCanvasScroller.scrollTo(0, 0);
- }
-
- // ---------------------------------------------------
- // Draw all TreeNode's in gTreeNodes[] on the gCanvas
- for (var i = 0; i < gTreeNodes.length; i++)
- {
- // Get the TreeNode
- tNode = gTreeNodes[i];
-
- // Don't draw TreeNode if its marked as hidden
- if (!tNode.hidden)
- {
- // Draw child connection lines for parent nodes
- // (best done first to avoid node box overlaps)
- if (tNode.child !== null)
- drawChildConnectionLinesFor(tNode);
-
- // Draw node box and the text inside it
- drawNodeBoxWithTextInside(tNode, true);
- }
- }
-
- // Set global flag to show that expanded image is cleared
- gExpandedNode = null;
- }
-
- // ===================================================
- // Scrolls Tab with passed tabID into view on screen
- // ===================================================
- function scrollTabIntoView(tabID)
- {
- // TreeNode and integers
- var tNode;
- var minX = 0;
- var maxX = 0;
- var tabScrollX;
-
- // --------------------------------------------------
- // Get (minX, maxX) for Tab sub-tree drawn on gCanvas
- for (var i = 1; i < gTreeNodes.length; i++)
- {
- // Only check visible TreeNodes with passed tabID
- tNode = gTreeNodes[i];
- if (!tNode.hidden && tNode.histNode.tab === tabID)
- {
- if (tNode.pos.x + tNode.width > maxX)
- maxX = tNode.pos.x + tNode.width;
-
- if (minX === 0 || tNode.pos.x < minX)
- minX = tNode.pos.x;
- }
- }
-
- // Scroll the Tab into view
- if (maxX <= CANVAS_MIN_WID)
- {
- // Tab fits on screen when scrolled to (0,0)
- tabScrollX = 0;
- }
- else
- {
- // Tab is off screen to the right
- if (maxX - minX > CANVAS_MIN_WID)
- tabScrollX = minX - 20;
- else
- tabScrollX = maxX - CANVAS_MIN_WID + 20;
- }
-
- // Scroll Tab into view. The setTimeout() is needed
- // because the scrollbar can take while to appear?!
- setTimeout(function()
- {
- clearTimeout(); // Essential
- gCanvasScroller.scrollTo(tabScrollX, 0);
- }, 100);
- }
-
- // ==============================================================
- // Draws all connection lines between a parent and her children
- // ==============================================================
- function drawChildConnectionLinesFor(node)
- {
- // Set line color and thickness for all connection lines
- gCtx.strokeStyle = "rgb(206,255,153)"; // Light green
- gCtx.lineWidth = 2; // Looks best
-
- // If only one child draw conn to parent, then exit
- var child = node.child;
- var centerX, vLineX;
-
- if (child.sibling === null)
- {
- centerX = Math.round(child.pos.x + BOX_WIDTH / 2);
- drawLine(centerX, child.pos.y, centerX, node.pos.y + node.height + 7);
- return;
- }
-
- // --------------------------------------------------------------
- // Initialize vars for multiple children connection line drawing
- var firstChild = node.child;
- var firstX = Math.round(child.pos.x + BOX_WIDTH / 2);
- var levelGap = Math.round((child.pos.y - (node.pos.y + node.height)) / 2);
- var vLineYend = child.pos.y - levelGap + 3; // Adjusts horiz bar Y pos
- var radius = 8; // Horizontal line corner curve size
-
- // Draw vertical conn lines for all siblings - except the last one
- while (child.sibling != null)
- {
- // Draw vertical conn line to center of level gap for this child
- centerX = Math.round(child.pos.x + BOX_WIDTH / 2);
- if (child === firstChild)
- drawLine(centerX, child.pos.y, centerX, vLineYend + radius - 1);
- else
- drawLine(centerX, child.pos.y, centerX, vLineYend);
-
- // Get next sibling
- child = child.sibling;
- }
-
- // Draw vertical conn line to center of level gap for last child
- centerX = Math.round(child.pos.x + BOX_WIDTH / 2);
- drawLine(centerX, child.pos.y, centerX, vLineYend + radius - 1);
-
- // ------------------------------------------------------------------
- // Draw horiz line above all children just below center of level gap
- drawLine(firstX + radius, vLineYend, centerX - radius, vLineYend);
-
- // Draw horiz line corner curves - first right, then left
- gCtx.beginPath();
- gCtx.moveTo(centerX - radius, vLineYend);
- gCtx.quadraticCurveTo(centerX, vLineYend, centerX, vLineYend + radius);
- gCtx.moveTo(firstX + radius, vLineYend);
- gCtx.quadraticCurveTo(firstX, vLineYend, firstX, vLineYend + radius);
- gCtx.stroke();
-
- // Draw vertical conn from center child to parent (middle of horiz line)
- vLineX = Math.round(firstX + (centerX - firstX) / 2);
- drawLine(vLineX, vLineYend, vLineX, node.pos.y + node.height + 7);
- }
-
- // ======================================================
- // Draws a TreeView node box and the text inside it
- // ======================================================
- function drawNodeBoxWithTextInside(tNode, showHighLight)
- {
- // Init vars
- var boxFill, bordCol;
-
- // ----------------------------------------------
- // Don't draw TreeNode if its marked as hidden
- if (!tNode.hidden)
- {
- // Set colours to use when drawing tree box
- if (showHighLight && isHighlightedNode(tNode))
- {
- // Highlight node found when user clicked Find Next/Prev
- boxFill = "rgb(184,225,174)"; // Light Green
- bordCol = "rgb(230,0,46)"; // Bright Red
- }
- else if (tNode.parent === null || !tNode.isOpenPage)
- {
- // "Dummy" root or node representing closed FF page
- boxFill = "rgb(200,200,200)"; // Light grey
- bordCol = "rgb(0,0,128)"; // Dark blue
- }
- else if (tNode.histNode === gParamArray[1])
- {
- // Page currently visible in current FF Tab
- boxFill = CURR_TAB_COL; // Orange/Red
- bordCol = "rgb(230,0,46)"; // Bright Red
- }
- else
- {
- // Open FF page (not root and not current FF page)
- boxFill = "rgb(250,250,250)"; // Off white
- bordCol = "rgb(2,102,0)"; // Dark green
- }
-
- // Draw node box using set colours
- drawNodeBox(tNode, boxFill, bordCol);
-
- // Draw text inside node
- drawDescriptionInNodeBox(tNode, "rgb(160,0,0)");
- }
- }
-
- // ==========================================================
- // Draws a TreeNode on the gCanvas - Representing a FF page
- // ==========================================================
- function drawNodeBox(node, boxFill, bordCol)
- {
- // Initialize box-button and box-panel drawing vars
- var panFill; // Top-panel colour
- var btnY, btnX, leftBtnWid; // Button drawing integers
- var btnFill = "rgb(200,200,200)"; // Button fill colour (grey)
- var textCol = "rgb(0,0,128)"; // Button text colour (blue)
- var txtNum; // Button text
-
- // -------------------------------------------------------
- // Draw node box as rounded-corner rect with inner border
- gCtx.lineWidth = 2;
- if (node.parent === null)
- {
- // "Dummy" root - Taller with light grey fill
- drawRoundedRectWithBorder
- (node.pos.x, node.pos.y - 8,
- node.width, node.height + 8,
- 9, "rgb(225,225,225)", bordCol);
- }
- else
- {
- // Not "Dummy" root - Shorter with passed boxFill
- drawRoundedRectWithBorder
- (node.pos.x, node.pos.y,
- node.width, node.height,
- 9, boxFill, bordCol);
- }
-
- // ---------------------------------------------------------
- // Draw top panel area if passed node is a Tab sub-tree root
- if (node.height > BOX_HEIGHT)
- {
- // Set header panel fill color depending on Tab root type
- if (node.histNode.tab === gCurrentTabID)
- panFill = CURR_TAB_COL; // Orange
- else if (node.inOpenTab)
- panFill = OPEN_TAB_COL; // Yellow
- else
- panFill = CLOSED_TAB_COL; // Blue
-
- // Draw Tab root node header panel
- drawTabHeader
- (node.pos.x + 1, node.pos.y + 1,
- node.width - 2, node.height - BOX_HEIGHT,
- 9, panFill, bordCol);
- }
-
- // ----------------------------------------------------------
- // Draw box-buttons at bottom of node box - with "up-arrow"
- // shape or text inside. First, set y for drawing all buttons
- btnY = Math.round(node.pos.y + node.height - (BOX_BTN_HGT / 2));
-
- // Draw one or two box-buttons
- gCtx.lineWidth = 2;
- if (node.child === null && node.hiddenTreeSize === 0)
- {
- // Leaf node - So only draw view web-page "?" button
- btnX = Math.round(node.pos.x + (node.width - BOX_BTN_WID) / 2);
- drawRoundedRectWithBorder
- (btnX, btnY, BOX_BTN_WID, BOX_BTN_HGT, 6, btnFill, bordCol);
-
- // Draw "?" inside view web-page button
- drawMessageOnCanvasAt
- (btnX + 7, btnY + 12, "?", "bold 9pt arial", textCol);
-
- // Set TreeNode props to enable fast detection of mouse clicks
- node.btnPanelX = btnX;
- node.hidBtnWid = 0;
- node.btnPanelWid = BOX_BTN_WID;
- }
- else
- {
- // Node with children - which can visible or hidden
- // Draw hidden sub-tree size or "up-arrow" inside button
- if (node.hiddenTreeSize > 1) // Hidden children
- {
- // Adjust left box width to allow printing of sub-tree size
- txtNum = (node.hiddenTreeSize - 1).toString();
- if ((node.hiddenTreeSize - 1) > 9)
- leftBtnWid = gCtx.mozMeasureText(txtNum) + 15; // Wider for > 9
- else
- leftBtnWid = BOX_BTN_WID; // Normal width for < 10
-
- // **** COMMENT THESE LINES IN TO TEST LARGE SUB-TREE SIZES ****
- //txtNum = (12345).toString();
- //leftBtnWid = gCtx.mozMeasureText(txtNum) + 15;
-
- // Draw button that shows hidden sub-tree size
- btnX = Math.round(node.pos.x + (node.width - leftBtnWid - BOX_BTN_WID) / 2);
- drawRoundedRectWithBorder
- (btnX, btnY, leftBtnWid, BOX_BTN_HGT, 6, btnFill, bordCol);
-
- // Draw hidden sub-tree size inside button
- drawMessageOnCanvasAt
- (btnX + 7, btnY + 11, txtNum, "7pt verdana", "rgb(230,0,0)");
- }
- else
- {
- // Node has no hidden children - Draw button with "up-arrow"
- leftBtnWid = BOX_BTN_WID;
- btnX = Math.round(node.pos.x + (node.width - BOX_BTN_WID * 2) / 2);
- drawRoundedRectWithBorder
- (btnX, btnY, BOX_BTN_WID, BOX_BTN_HGT, 6, btnFill, bordCol);
-
- // Draw blue filled "up arrow" shape
- gCtx.beginPath();
- gCtx.moveTo(btnX + 5, btnY + 11);
- gCtx.lineTo(btnX + 16, btnY + 11);
- gCtx.lineTo(btnX + 10, btnY + 4);
- gCtx.fillStyle = textCol;
- gCtx.fill();
- }
-
- // Set TreeNode props to enable fast detection of user mouse clicks
- node.btnPanelX = btnX;
- node.hidBtnWid = leftBtnWid;
- node.btnPanelWid = leftBtnWid + BOX_BTN_WID;
-
- // Draw button used to show web-page image
- btnX += leftBtnWid;
- drawRoundedRectWithBorder
- (btnX, btnY, BOX_BTN_WID, BOX_BTN_HGT, 6, btnFill, bordCol);
-
- // Draw "?" inside button
- drawMessageOnCanvasAt
- (btnX + 7, btnY + 12, "?", "bold 9pt arial", textCol);
- }
- }
-
- // =======================================================
- // Draw TreeNode description lines inside TreeNode box
- // =======================================================
- function drawDescriptionInNodeBox(node, txtCol)
- {
- // Text and time info appearance
- var timeCol = "rgb(0,0,110)"; // Dark blue
- var txtFont = "bold 7pt verdana"; // Bold
- var timeFont = "7pt verdana"; // Not bold
- var timeInfo = "";
- var tabHdrMsg = "";
- var yAddOn = 0;
-
- // -------------------------------------------------
- // Draw session info, then exit if node is tree root
- if (node.histNode === null)
- {
- drawSessionInfoInTreeRootBox(node);
- return;
- }
-
- // Rebuild TreeNode.descLine's if not set, or need updating
- if (node.histNodeDesc !== node.histNode.desc
- || node.histNode.desc === "")
- {
- setDescriptionLinesFor(node, txtFont);
- node.histNodeDesc = node.histNode.desc;
- }
-
- // -------------------------------------------------
- // Draw text in Tab root node box header panel
- if (node.height > BOX_HEIGHT)
- {
- // Set header panel text depending on Tab root type
- if (node.histNode.tab === gCurrentTabID)
- tabHdrMsg = "Current";
- else if (node.inOpenTab)
- tabHdrMsg = "Open";
- else
- tabHdrMsg = "Closed";
-
- // Add number of TreeNode's in Tab sub-tree to header text
- tabHdrMsg += " - " + (node.tabTreeSize).toString();
- if (node.tabTreeSize > 1)
- tabHdrMsg += " pages";
- else
- tabHdrMsg += " page";
-
- // Draw text in panel and set yAddOn for text below
- drawNodeText(node, 14, tabHdrMsg, txtFont, "black");
- yAddOn = node.height - BOX_HEIGHT;
- }
-
- // ---------------------------------------------
- // Get time web-page was first loaded into FF
- timeInfo = node.histNode.timeInfo;
-
- // Make txtCol red darker for current FF page
- if (node.histNode === gParamArray[1])
- txtCol = "rgb(120,0,0)";
-
- // Draw one or two lines of text at hard coded Y
- if (node.descLine2 === "")
- {
- // Draw line1 only
- drawNodeText(node, 25 + yAddOn, node.descLine1, txtFont, txtCol);
-
- // Draw time info
- drawNodeText(node, 40 + yAddOn, timeInfo, timeFont, timeCol);
- }
- else
- {
- // Draw both lines
- drawNodeText(node, 19 + yAddOn, node.descLine1, txtFont, txtCol);
- drawNodeText(node, 32 + yAddOn, node.descLine2, txtFont, txtCol);
-
- // Draw time info
- drawNodeText(node, 47 + yAddOn, timeInfo, timeFont, timeCol);
- }
- }
-
- // =============================================================
- // Draws header panel for all Tab sub-tree root TreeNode's
- // =============================================================
- function drawTabHeader(x, y, w, h, radius, fillCol, bordCol)
- {
- // Draw filled rectangle with rounded top corners
- gCtx.beginPath();
- gCtx.moveTo(x, y + radius);
- gCtx.lineTo(x, y + h);
- gCtx.lineTo(x + w, y + h);
- gCtx.lineTo(x + w, y + radius);
- gCtx.quadraticCurveTo(x + w, y, x + w - radius, y);
- gCtx.lineTo(x + radius, y);
- gCtx.quadraticCurveTo(x, y, x, y + radius);
- gCtx.fillStyle = fillCol;
- gCtx.fill();
-
- // Draw a line below the rectangle
- gCtx.lineWidth = 1;
- gCtx.beginPath();
- gCtx.moveTo(x, y + h);
- gCtx.lineTo(x + w, y + h);
- gCtx.strokeStyle = bordCol;
- gCtx.stroke();
- }
-
- // ===========================================================
- // Draw page-visited and Tab-count stats in "dummy" tree root
- // ===========================================================
- function drawSessionInfoInTreeRootBox(node)
- {
- // Text and time info appearance
- var infoLen, numOpen, printX; // Integers
- var txtCol = "rgb(160,0,0)"; // Dark red
- var txtFont = "bold 7pt verdana"; // Bold
- var infoFont = "7pt verdana"; // Not bold
- var infoStat = ""; // See below
-
- // X to print at depends on variable width of num pages
- gCtx.mozTextStyle = infoFont;
- infoStat = (gTreeNodes.length - 1).toString();
- infoLen = gCtx.mozMeasureText(infoStat);
- printX = Math.round(node.pos.x + (BOX_WIDTH - infoLen) / 2);
-
- // Draw info labels in info box
- gCtx.mozTextStyle = txtFont;
- gCtx.fillStyle = txtCol;
- fillText("Pages visited:", printX - 41, node.pos.y + 15);
- fillText("Tabs now open:", printX - 42, node.pos.y + 29);
- fillText("Tabs closed:", printX - 42, node.pos.y + 43);
-
- // Draw info counts in info box
- gCtx.mozTextStyle = infoFont;
- gCtx.fillStyle = "rgb(0,0,110)";
-
- // Num pages visited is size of gTreeNodes[] - 1
- infoStat = (gTreeNodes.length - 1).toString();
- fillText(infoStat, printX + 42, node.pos.y + 15);
-
- // Show count of number of open Tabs in gTabRoots[]
- numOpen = numExtWinOpenTabs();
- infoStat = numOpen.toString();
- fillText(infoStat, printX + 42, node.pos.y + 29);
-
- // Show number of closed Tabs in gTabRoots[]
- infoStat = (gTabRoots.length - numOpen).toString();
- fillText(infoStat, printX + 42, node.pos.y + 43);
- }
-
- // ===========================================================
- // Draws FF session info box below tree root TreeNode box
- // Drawn instead of expanded image, for tree root node only
- // ===========================================================
- function drawSessInfoBox(node)
- {
- // Calculate (x,y) position to show info box at
- var infoBoxHgt = 85;
- var infoBoxWid = 192;
- var x = Math.round(node.pos.x - (infoBoxWid - node.width) / 2);
- var y = Math.round(node.pos.y + node.height + BOX_BTN_HGT / 2 + 2);
-
- // Save global vars used to detect mouse-clicks on info box
- gImgXY = new Point(x, y);
- gInfoBoxWH = new Point(infoBoxWid, infoBoxHgt);
-
- // -------------------------------------------------
- // Drawing colours and fonts
- var panFill = "rgb(180,190,220)"; // Blue
- var boxFill = "rgb(225,225,225)"; // Light Grey
- var bordCol = "rgb(0,0,128)"; // Dark blue
- var txtCol = "rgb(160,0,0)"; // Dark red
- var txtFont = "bold 7pt verdana"; // Bold
- var infoFont = "7pt verdana"; // Not bold
-
- // Session timing information vars
- var sessStartDate = Application.storage.get
- ("FFstartDate", new Date()); // Set when FF starts
- var sessTimeInfo; // Drawn in info box
-
- // -------------------------------------------------
- // Draw info box
- gCtx.lineWidth = 2;
- drawRoundedRectWithBorder
- (x, y, infoBoxWid, infoBoxHgt, 9, boxFill, bordCol);
-
- // Draw top panel area
- drawTabHeader
- (x + 1, y + 1, infoBoxWid - 2, 20, 9, panFill, bordCol);
-
- // Draw label in top panel area
- gCtx.mozTextStyle = txtFont;
- gCtx.fillStyle = txtCol;
- fillText("Browsing Session Times", x + 38, y + 15);
-
- // Draw info labels in box
- fillText("Start date:", x + 20, y + 41);
- fillText("Start time:", x + 20, y + 56);
- fillText("Duration:", x + 20, y + 71);
-
- // Draw start date - e.g. "Fri Apr 24 2009" etc.
- gCtx.mozTextStyle = infoFont;
- gCtx.fillStyle = "rgb(0,0,110)";
- sessTimeInfo = sessStartDate.toDateString();
- fillText(sessTimeInfo, x + 84, y + 41);
-
- // Draw start time - e.g. "16:45" etc.
- sessTimeInfo = sessStartDate.toTimeString();
- fillText(sessTimeInfo.substr(0,5), x + 84, y + 56);
-
- // Draw session duration - e.g. "1 hour 2 min" etc.
- sessTimeInfo = browsingSessionDuration(sessStartDate);
- fillText(sessTimeInfo, x + 84, y + 71);
- }
-
- // =========================================================
- // Constructs and returns Firefox browsing session duration
- // =========================================================
- function browsingSessionDuration(sessStartDate)
- {
- // Init vars
- var timeNow = new Date();
- var secsNow, startedSecs;
- var minSpent, hourSpent, minOver;
-
- // ---------------------------------------------
- // Get time to nearest second from both dates
- secsNow = Math.round(timeNow.getTime() / 1000);
- startedSecs = Math.round(sessStartDate.getTime() / 1000);
-
- // Subtract times to get min spent (to nearest 0.1 of minute)
- minSpent = ((secsNow - startedSecs) / 60).toFixed(1);
-
- // Construct and return browsing session duration
- if (minSpent > 60)
- {
- // More than 60 mins - Format is "1 hour 1 min" etc.
- hourSpent = Math.floor(minSpent / 60);
- minOver = Math.floor(minSpent - hourSpent * 60);
- return hourSpent.toString() + " hour "
- + minOver.toString() + " min";
- }
- else if (minSpent === 0)
- {
- // Less than 6 seconds - Format is "5 seconds" etc.
- return (secsNow - startedSecs).toString() + " seconds";
- }
- else
- {
- // Up to 60 mins - Format is "1.2 min" etc.
- return minSpent.toString() + " min";
- }
- }
-
- // ============================================================
- // Sets node.descLine1 and node.descLine2 for passed TreeNode
- // ============================================================
- function setDescriptionLinesFor(node, font)
- {
- // Get trimmed desc or URI from associated HistoryNode
- var txt = trim(node.histNode.desc);
- if (txt === "") // Use URI instead
- txt = trim(node.histNode.uri);
-
- // Set both node lines then exit if trimmed desc fits on one line
- var maxWid = BOX_WIDTH - 26;
- gCtx.mozTextStyle = font;
- if (gCtx.mozMeasureText(txt) <= maxWid)
- {
- node.descLine1 = txt;
- node.descLine2 = "";
- return;
- }
-
- // -------------------------------------------------------
- // Descrip does NOT fit on one line - So create two lines
- var txtLen = txt.length;
- var chrNum = 0;
- var lastSepNdx = 0;
- var line1 = "";
- var chr = "";
- var tempTxt = "";
-
- // Keep adding chars to line1 until its width >= maxWid
- while (gCtx.mozMeasureText(line1) < maxWid && chrNum < txtLen)
- {
- chr = txt.substr(chrNum, 1);
- line1 += chr;
-
- if (isSeperator(chr))
- lastSepNdx = chrNum;
-
- chrNum ++;
- }
-
- // -----------------------------------------------------
- // Set both TreeNode lines depending on result of above
- if (lastSepNdx === 0)
- {
- // Description is just one very long word
- node.descLine1 = truncatedText(txt, maxWid);
- node.descLine2 = "";
- }
- else
- {
- // Remove last chr if it made line1 too wide
- if (gCtx.mozMeasureText(line1) > maxWid)
- {
- chrNum --;
- line1 = txt.substr(0, chrNum);
- chr = txt.substr(chrNum - 1, 1)
- }
-
- // Change the end part of line1 as req
- tempTxt = txt.substr(chrNum, 1);
- if (!isSeperator(tempTxt) && !isSeperator(chr))
- // Remove partial word from end of line1
- line1 = txt.substr(0, lastSepNdx + 1);
- else if (isSeperator(tempTxt))
- // Add seperator to end of line1 (looks best)
- line1 = txt.substr(0, chrNum + 1);
-
- // Set line2 = truncated version of remainder of desc
- node.descLine1 = trim(line1);
- tempTxt = txt.substr(node.descLine1.length);
- node.descLine2 = truncatedText(tempTxt, maxWid);
- }
- }
-
- // =======================================================
- // Returns true if passed chr is defined as a seperator
- // =======================================================
- function isSeperator(chr)
- {
- // Seperators could be as below (or not? or other?)
- if (chr === " " || chr === "/" || chr === ","
- || chr === ":" || chr === ";" || chr === "_")
- return true;
- else
- return false;
- }
-
- // ============================================================
- // Draws passed txt centrally inside box drawn for passed node
- // ============================================================
- function drawNodeText(node, yAddOn, txt, font, col)
- {
- // Centralize txt horizontally
- gCtx.mozTextStyle = font;
- var txtLen = gCtx.mozMeasureText(txt);
- var x = Math.round(node.pos.x + (BOX_WIDTH - txtLen) / 2);
-
- // Add passed increment to y
- var y = node.pos.y + yAddOn;
-
- // Draw txt inside node box
- gCtx.fillStyle = col;
- fillText(txt, x, y);
- }
-
- // ==================================================================================
- // FIRST - Called from function respondToTreeViewMouseClickOnCanvas(x, y)
- // ----------------------------------------------------------------------------------
- // THEN - Called repeatedly via setInterval() during ".jpeg" image expansion
- // FINALLY - Terminates itself via clearInterval() when ".jpeg" width >= MAX_IMG_WID
- // ==================================================================================
- function drawExpandingNodeImageOnCanvas(node)
- {
- // Exit if passed TreeNode is "dummy" tree root
- if (node.histNode === null)
- return;
-
- // ------------------------------------------------------
- // Constant used to increase image size *** NOTE Some
- // PC's have slow graphics - So use 10 steps of 75 to 750
- const ADD_IMG_WID = 75;
-
- // Init vars
- var x, y; // Integers
- var maxWidX, imgHgt; // Integers
- var histNode; // HistoryNode
-
- // -------------------------------------------------
- // Calculate (x,y) position to show image at
- x = Math.round(node.pos.x - (gNodeImageWid + ADD_IMG_WID - node.width) / 2);
- y = Math.round(node.pos.y + node.height + BOX_BTN_HGT / 2 + 2);
-
- // Adjust drawn image x co-ordinate if it goes off-screen
- // *** NOTE - This does NOT consider scrolled position
- maxWidX = Math.round(node.pos.x - (MAX_IMG_WID - node.width) / 2);
-
- if (maxWidX + MAX_IMG_WID > gCanvas.width)
- x -= Math.round((maxWidX + MAX_IMG_WID) - gCanvas.width) + 20;
- else if (maxWidX <= 5)
- x += Math.abs(maxWidX) + 20;
-
- // -------------------------------------------------------
- // Draw image or blank rectangle if animation not complete
- if (gNodeImageWid < MAX_IMG_WID)
- {
- // Increase image size (produces animation effect)
- gNodeImageWid += ADD_IMG_WID;
- imgHgt = Math.round(gNodeImageWid * WH_RATIO);
-
- // Draw histNode ".jpeg" image or a white bordered rectangle
- histNode = node.histNode;
- if (!blankRectangleDrawn
- (histNode, x, y, gNodeImageWid, imgHgt, false, "black"))
- {
- // Draw a black inner border to contain the image
- gCtx.lineWidth = 2;
- gCtx.strokeStyle = 'black';
- gCtx.strokeRect(x - 1, y - 1, gNodeImageWid + 2, imgHgt + 2);
-
- // Draw ".jpeg" image - i.e page HAS been viewed in FF
- if (!histNode.imgCreated)
- {
- // Slow draw - once only
- drawAndUpdateNodeImage
- (histNode, x, y, gNodeImageWid, imgHgt);
- }
- else
- {
- // Fast draw - all further draws
- gCtx.drawImage
- (histNode.imgToDraw, x, y, gNodeImageWid, imgHgt);
- }
- }
-
- // Save global var used to detect mouse-clicks on image
- gImgXY = new Point(x, y);
- }
- else
- {
- // Stop setInterval() and set global flag
- clearInterval(gImgExpandInterval);
- gImgExpandInterval = null;
-
- // Reset global flag to show that animation has stopped
- gNodeImageWid = 0;
-
- // Save global var used to detect mouse-clicks on image
- gInfoBoxWH = new Point(MAX_IMG_WID, MAX_IMG_WID * WH_RATIO);
- }
- }
-
- // ===========================================================
- // Draws a rounded rect with inner border (inner looks best)
- // ===========================================================
- function drawRoundedRectWithBorder
- (x, y, w, h, radius, fillCol, bordCol)
- {
- // Draw a rounded corner rectangle
- drawRoundedRect(x, y, w, h, radius, fillCol, true);
-
- // Draw an inner border within rectangle
- drawRoundedRect(x, y, w, h, radius, bordCol, false);
- }
-
- // =================================================================
- // Draws a filled/unfilled rounded corner rect using passed params
- // =================================================================
- function drawRoundedRect(x, y, w, h, radius, col, fill)
- {
- // Draw a rounded corner rectangle using passed params
- gCtx.beginPath();
- gCtx.moveTo(x, y + radius);
- gCtx.lineTo(x, y + h - radius);
- gCtx.quadraticCurveTo(x, y + h, x + radius, y + h);
- gCtx.lineTo(x + w - radius, y + h);
- gCtx.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
- gCtx.lineTo(x + w, y + radius);
- gCtx.quadraticCurveTo(x + w, y, x + w - radius, y);
- gCtx.lineTo(x + radius, y);
- gCtx.quadraticCurveTo(x, y, x, y + radius);
-
- // Fill or just outline the rectangle
- if (fill)
- {
- gCtx.fillStyle = col;
- gCtx.fill();
- }
- else
- {
- gCtx.strokeStyle = col;
- gCtx.stroke();
- }
- }
-
- // ===========================================================
- // Draws a filled rect with inner border using passed params
- // ===========================================================
- function drawBorderedRect(x, y, w, h, fillCol, bordCol, bordWid)
- {
- // Draw filled rectangle
- gCtx.fillStyle = fillCol;
- gCtx.fillRect(x, y, w, h);
-
- // Draw rectangle border
- gCtx.lineWidth = bordWid;
- gCtx.strokeStyle = bordCol;
- gCtx.strokeRect(x, y, w, h);
- }
-
- // ====================================================
- // Draws a line on the gCanvas from (xf,yf) to (xt,yt)
- // ====================================================
- function drawLine(xf, yf, xt, yt)
- {
- gCtx.beginPath();
- gCtx.moveTo(xf, yf);
- gCtx.lineTo(xt, yt);
- gCtx.stroke();
- }
-
- // ======================================================
- // Draws the passed msg on canvas at (x,y) in passed col
- // ======================================================
- function drawMessageOnCanvasAt(x, y, msg, font, col)
- {
- // Show test message in passed colour at specified (x,y)
- gCtx.fillStyle = col;
- gCtx.mozTextStyle = font;
- fillText(msg, x, y);
- }